/**
 * 
 */
package org.vsg.common.pagination.apps;

import java.net.MalformedURLException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.vsg.common.pagination.AdvancedPagination;
import org.vsg.common.pagination.PageIndexOutOfBoundsException;
import org.vsg.common.pagination.Pagination;
import org.vsg.common.pagination.PaginationConstant;
import org.vsg.common.pagination.RecordPaginationException;
import org.vsg.common.pagination.carrier.CarrierCreatedObjectException;
import org.vsg.common.pagination.carrier.CarrierNotSupportedException;
import org.vsg.common.pagination.carrier.CarrierValueDefinedException;
import org.vsg.common.pagination.carrier.GroupingValueCarrier;
import org.vsg.common.pagination.carrier.RecordLimitCarrier;
import org.vsg.common.pagination.carrier.ValueCarrier;
import org.vsg.common.pagination.carrier.WebCarrier;
import org.vsg.common.pagination.keyword.PaginationKeyword;
import org.vsg.common.pagination.keyword.PaginationKeywordException;
import org.vsg.common.pagination.keyword.PaginationKeywordFilter;
import org.vsg.common.pagination.sort.SingleSort;
import org.vsg.common.pagination.sort.SortField;
import org.vsg.common.pagination.wrapper.AdvancedGenericWebSortByPaginationWrapper;
import org.vsg.common.pagination.wrapper.AdvancedWebPaginationWrapper;
import org.vsg.common.pagination.wrapper.GenericWebPaginationWrapper;
import org.vsg.common.pagination.wrapper.PaginationWrapper;
import org.vsg.common.pagination.wrapper.PaginationWrapperException;
import org.vsg.common.pagination.wrapper.SortFieldPaginationWrapper;
import org.vsg.common.pagination.wrapper.WebPaginationWrapper;

/**
 * The class is set for the request from client.
 * @author bill vison
 * 
 */
public class HttpPaginationApplicationContext implements PaginationApplicationContext , ValueCarrierApplicationContext , RecordLimitCarrierApplicationContext , HttpApplicationContext {
	
	final static Logger logger = LoggerFactory.getLogger(HttpPaginationApplicationContext.class);

	/**
	 * --- set the environment properties 
	 */
	private Properties envProperties;
	
	private Map parameters = new HashMap();
	
	private HttpServletRequest request;


	/**
	 * the default cast to cass for parameter set to be String class
	 * @param parameterName
	 */
	public void addParameterName(String parameterName) throws PaginationKeywordException{
		
		// validate parameter name 
		addParameterName(parameterName, String.class);
	}
	
	/**
	 * 
	 * @param parameterName
	 * @param castToClass channge the class
	 * @throws PaginationKeywordException
	 */
	public void addParameterName(String parameterName , Class castToClass) throws PaginationKeywordException {

		if (parameterName.equals(PaginationKeyword.GOTOPAGE_NAME)
				|| 	parameterName.equals(PaginationKeyword.SINGLESORTBY_FIELD)
				) {
				String msg = "You could not defined some following keyword.\n";
				msg += "such as " + "\""+PaginationKeyword.GOTOPAGE_NAME + "\" ," + "\""+PaginationKeyword.SINGLESORTBY_FIELD+"\".\n";
				msg += "please use another parameter name!";
				throw new PaginationKeywordException(msg);
			}
		
		// add the parameter to ths class 
		parameters.put(parameterName, castToClass);
	}
	
	/**
	 * 
	 * @param parameterName
	 */
	public void removeParameterName(String parameterName) {
		if ( parameters.containsKey(parameterName) ) {
			Object classValue = parameters.remove(parameterName);
			this.logger.debug("remove parameter '"+parameterName+"' from pagination application! ");
		}
	}
	

	public PaginationWrapper generatePaginatonWrapper(Pagination page) throws PaginationWrapperException {
		PaginationWrapper paginationWrapper = null;
		
		if (page instanceof AdvancedPagination) {
			paginationWrapper = new AdvancedGenericWebSortByPaginationWrapper();
		}
		else {
			paginationWrapper = new GenericWebPaginationWrapper();
		}
		
		// load pagination
		paginationWrapper.loadPagination(page);		
		
		String url = request.getRequestURI();

		// --------------------------------		
		
		// defined value for WebPaginationWrapper
		if ( paginationWrapper instanceof WebPaginationWrapper) {
			WebPaginationWrapper wpageWrper = (WebPaginationWrapper)paginationWrapper;
			// predefine variable
			wpageWrper.setUrl(url);
			wpageWrper.setGoToPageParamName(PaginationKeyword.GOTOPAGE_NAME);

			PaginationKeywordFilter keywordFilter = PaginationKeywordFilter.getInstance();

			String parameterName = null;
			
			Set parameters = this.parameters.keySet();
			
			// parse parameter from http request
			for (Iterator iter = parameters.iterator() ; iter.hasNext();) {
				parameterName = (String)iter.next();
				if (!keywordFilter.isKeyword(parameterName)) {
					
					// parse parameter from http request
					String[] parameterValues = request.getParameterValues(parameterName);
					if (parameterValues != null) {
						// add the parameter to the request url
						wpageWrper.addParamToUrl(parameterName , parameterValues);
					}
					
				}
			}		
		}
		
		// --------------------------------
		// defined value for SortFieldPaginationWrapper		
		if (paginationWrapper instanceof SortFieldPaginationWrapper) {
			SortFieldPaginationWrapper sortPaginationWrapper = (SortFieldPaginationWrapper)paginationWrapper;

			String sortByField = request.getParameter(PaginationKeyword.SINGLESORTBY_FIELD);
			if (sortByField != null) {
				String[] sortByFieldArrays = sortByField.split(" ");
				
				SortField sortField = new SortField();
				// field value
				sortField.setField(sortByFieldArrays[0].trim());
				
				// sort by type
				if (sortByFieldArrays.length == 2) {
					if (sortByFieldArrays[1].trim().equalsIgnoreCase("desc")) {
						sortField.setSortOrder(SortField.DESCENDING);
					}
					else if (sortByFieldArrays[1].trim().equalsIgnoreCase("asc")){
						sortField.setSortOrder(SortField.ASCENDING);
					}
				} else {
					sortField.setSortOrder(SortField.ASCENDING);
				}
				sortPaginationWrapper.setSortField(sortField);
			}
		}
		
		// --------------------------------
		// defined value for AdvancedWebPaginationWrapper
		if (paginationWrapper instanceof AdvancedWebPaginationWrapper) {
			AdvancedWebPaginationWrapper advWebPaginationWrapper = (AdvancedWebPaginationWrapper)paginationWrapper;
			advWebPaginationWrapper.setGoToGroupingParamName(PaginationKeyword.GROUPING_FIELD);
		}
		
		return paginationWrapper;
	}	
	
	
	/**
	 * @return Returns the request.
	 */
	public HttpServletRequest getRequest() {
		return request;
	}

	/**
	 * @param request The request to set.
	 */
	public void setRequest(HttpServletRequest request) {
		this.request = request;
	}

	/* (non-Javadoc)
	 * @see org.vsg.commons.util.pagination.apps.PaginationApplication#initializeConfiguration()
	 */
	public void initializeConfiguration() throws PaginationApplicationContextException {
		// TODO Auto-generated method stub
		
		// --- check page size --- 
		String envPageSize = envProperties.getProperty(EnvironmentProperty.PAGE_SIZE);
		int pageSize = 0;
		if (envPageSize != null) {
			try {
				pageSize = Integer.parseInt(envPageSize);
			} catch (NumberFormatException e) {
				// TODO Auto-generated catch block
				logger.warn("Get the pageSize from Environment Properties : " + envPageSize);
				e.printStackTrace();
			}
		}
		
		// --- check group size ---
		String envGroupingSize = this.getConfiguration().getProperty(EnvironmentProperty.GROUPING_SIZE);
		int groupingSize = 0;
		if (envGroupingSize != null) {
			try {
				groupingSize = Integer.parseInt(envGroupingSize);
			} catch (NumberFormatException e) {
				// TODO Auto-generated catch block
				logger.warn("Get the groupingSize from Environment Properties : " + envGroupingSize);
				e.printStackTrace();
			}
			
		}
		
		// --- check limit size ---
		String envLimitSize = this.getConfiguration().getProperty(EnvironmentProperty.LIMIT);
		int limitSize = 0;
		if (envLimitSize != null) {
			try {
				limitSize = Integer.parseInt(envLimitSize);
			} catch (NumberFormatException e) {
				// TODO Auto-generated catch block
				logger.warn("Get the limit from Environment Properties : " + envLimitSize);
				e.printStackTrace();
			}
			
		}
	
	}

	/* (non-Javadoc)
	 * @see org.vsg.commons.util.pagination.apps.PaginationApplication#generateValueCarrier(java.lang.Class)
	 */
	public ValueCarrier generateValueCarrier(Class cls) throws CarrierNotSupportedException , CarrierValueDefinedException , CarrierCreatedObjectException{
		// TODO Auto-generated method stub
		
		Object obj  = null;
		
		try {
			obj = cls.newInstance();
		} catch (InstantiationException e1) {
			// TODO Auto-generated catch block
			throw new CarrierCreatedObjectException(e1);
		} catch (IllegalAccessException e1) {
			// TODO Auto-generated catch block
			throw new CarrierCreatedObjectException(e1);
		}
		
		
		// ---- check the base on carrier
		if (!(obj instanceof WebCarrier)) {
			throw new CarrierNotSupportedException("Class "+cls+" is not supported to WebValueCarrier Interface! ");
		}
		
		// --- get pagination key ---
		PaginationKeywordFilter keywordFilter = PaginationKeywordFilter.getInstance();
		
		// --- set WebValueCarrier value ---
		if (obj instanceof WebCarrier ) {
			
			WebCarrier wvc = (WebCarrier)obj;
			
			initWebCarrierValue(wvc , request , keywordFilter , this.parameters);

		}
		
		
		//	--- set SingleSort value ---
		if (obj instanceof SingleSort) {

			SingleSort ss = (SingleSort)obj;
			
			initSingleSortValue(ss , request );

		}
		
		
		// --- set AdvancedValueCarrier value ---
		if (obj instanceof GroupingValueCarrier) {
			
			GroupingValueCarrier avc = (GroupingValueCarrier)obj;
			
			initAdvancedValueCarrierValue(avc , request );
			
		}

		// --- set ValueCarrier value ---		
		if (obj instanceof ValueCarrier) {
			ValueCarrier vc = (ValueCarrier)obj;
			
			try {
				
				initValueCarrierValue(vc , request);
			
			} catch (PageIndexOutOfBoundsException e) {
				// TODO Auto-generated catch block
				throw new CarrierValueDefinedException(e);
			}
			
		}

		return (ValueCarrier)obj;
	}

	public RecordLimitCarrier generateRecordLimitCarrier(Class cls) throws CarrierCreatedObjectException , CarrierNotSupportedException , CarrierValueDefinedException{
		// TODO Auto-generated method stub
		Object obj  = null;
		
		try {
			obj = cls.newInstance();
		} catch (InstantiationException e1) {
			// TODO Auto-generated catch block
			throw new CarrierCreatedObjectException(e1);
		} catch (IllegalAccessException e1) {
			// TODO Auto-generated catch block
			throw new CarrierCreatedObjectException(e1);
		}
		
		
		// ---- check the base on carrier
		if (!(obj instanceof WebCarrier)) {
			throw new CarrierNotSupportedException("Class "+cls+" is not supported to WebValueCarrier Interface! ");
		}
		
		// --- get pagination key ---
		PaginationKeywordFilter keywordFilter = PaginationKeywordFilter.getInstance();
		
		// --- set WebValueCarrier value ---
		if (obj instanceof WebCarrier ) {
			
			WebCarrier wvc = (WebCarrier)obj;
			
			initWebCarrierValue(wvc , request , keywordFilter , this.parameters);

		}
		
		
		//	--- set SingleSort value ---
		if (obj instanceof SingleSort) {

			SingleSort ss = (SingleSort)obj;
			
			initRecordLimitSingleSortValue(ss , request );

		}
		
		

		// --- set ValueCarrier value ---		
		if (obj instanceof RecordLimitCarrier) {
			RecordLimitCarrier rlc = (RecordLimitCarrier)obj;
			
			try {
				initRecordLimitCarrier(rlc , request);
			} catch (PageIndexOutOfBoundsException e) {
				// TODO Auto-generated catch block
				throw new CarrierValueDefinedException( e );
			}
			
		}
		
		return (RecordLimitCarrier)obj;
	}	
	
	private void setParameterObjectValue(WebCarrier wvc , String value , String parameterName) {
		Class className = (Class)this.parameters.get(parameterName);
		
		
		java.io.Serializable objectValue = null;
		
		if (className.getName().equals(Boolean.class.getName())) {
			objectValue = new Boolean(value);
		}
		
		else if (className.getName().equals(Byte.class.getName())) {
			objectValue = new Byte(value);
		}
		
		else if (className.getName().equals(Double.class.getName())) {
			objectValue = new Double(value);
		}
		
		else if (className.getName().equals(Float.class.getName())) {
			objectValue = new Float(value);
		}
		
		else if (className.getName().equals(Integer.class.getName())) {
			objectValue = new Integer(value);
		}
		
		else if (className.getName().equals(Long.class.getName())) {
			objectValue = new Long(value);
		}
		
		else if (className.getName().equals(Short.class.getName())) {
			objectValue = new Short(value);
		}
		
		else if (className.getName().equals(Character.class.getName())) {
			char c = value.charAt(0);
			objectValue = new Character(c);
		}
		
		else if (className.getName().equals(java.sql.Date.class.getName())) {
			objectValue = java.sql.Date.valueOf(value);
		}
		
		else if (className.getName().equals(java.sql.Timestamp.class.getName())) {
			objectValue = java.sql.Timestamp.valueOf(value);
		}
		
		else if (className.getName().equals(java.sql.Time.class.getName())) {
			objectValue = java.sql.Time.valueOf(value);
		}
		
		else if (className.getName().equals(java.net.URL.class.getName())) {
			try {
				objectValue = new java.net.URL(value);
			} catch (MalformedURLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		
		else if (className.getName().equals(java.math.BigDecimal.class.getName())) {
			objectValue = new java.math.BigDecimal(value);
		}
		
		else if (className.getName().equals(java.math.BigInteger.class.getName())) {
			objectValue = new java.math.BigInteger(value);
		}
		
		else {
			objectValue = value;
		}
		wvc.setParameterObject(parameterName, objectValue);
		
	}

	private void setPrameterPrimitiveValue(WebCarrier wvc ,  String parameterName) {
		Class className = (Class)this.parameters.get(parameterName);
		
		
		java.io.Serializable objectValue = null;
		
		if (className.getName().equals(Boolean.class.getName())) {
			objectValue = new Boolean(false);
		}

		else if (className.getName().equals(Byte.class.getName())) {
			objectValue = new Byte((byte)0);
		}
		
		else if (className.getName().equals(Double.class.getName())) {
			objectValue = new Double(0);
		}
		
		else if (className.getName().equals(Float.class.getName())) {
			objectValue = new Float(0);
		}
		
		else if (className.getName().equals(Integer.class.getName())) {
			objectValue = new Integer(0);
		}
		
		else if (className.getName().equals(Long.class.getName())) {
			objectValue = new Long(0);
		}
		
		else if (className.getName().equals(Short.class.getName())) {
			objectValue = new Short((short)0);
		}
		
		else if (className.getName().equals(Character.class.getName())) {
			char c = ' ';
			objectValue = new Character(c);
		}
		
		else if (className.getName().equals(java.math.BigDecimal.class.getName())) {
			objectValue = new java.math.BigDecimal(0);
		}
		
		wvc.setParameterObject(parameterName, objectValue);
		
		
	}

	// --- initialize defined different carrier value ---
	
	// ### defined advance carrier value interface ###
	private void initAdvancedValueCarrierValue(GroupingValueCarrier avc , HttpServletRequest request) {
		int groupingSize = Integer.parseInt( envProperties.getProperty(EnvironmentProperty.GROUPING_SIZE) );
		avc.setGroupingSize( groupingSize );
		
		// parse special parameter
		String gotoGrouping = request.getParameter(PaginationKeyword.GROUPING_FIELD);
		if (gotoGrouping != null) {
			int gtGrouping = Integer.parseInt(gotoGrouping);
			gtGrouping = (gtGrouping - 1 > -1)?(gtGrouping - 1):gtGrouping;

			int pageIndex = gtGrouping * groupingSize;
			try {
				
				avc.setPageIndex(pageIndex);
			
			} catch (PageIndexOutOfBoundsException e) {
				// TODO Auto-generated catch block
				logger.warn( e.getLocalizedMessage() );
				
				try {
					avc.setPageIndex(0);
					
				} catch (PageIndexOutOfBoundsException e1) {
					// TODO Auto-generated catch block
				}				
			}
		}			
	}
	
	private void initValueCarrierValue(ValueCarrier vc , HttpServletRequest request) throws PageIndexOutOfBoundsException {
		
		// --- get the page size from properties ---
		int pageSize = Integer.parseInt( envProperties.getProperty(EnvironmentProperty.PAGE_SIZE) );

		vc.setPageSize(pageSize);

		// parse special parameter
		String gotoPage = request.getParameter(PaginationKeyword.GOTOPAGE_NAME);
		
		// get the gotopage request parameter
		if (gotoPage != null) {
			
			int gotopage = 0;
			try {
				gotopage = Integer.parseInt(gotoPage);
			} catch (NumberFormatException e2) {
				// TODO Auto-generated catch block
				logger.info("gotopage number is error! please check it!");
				gotopage = -1;
			}
			
			int pageIndex = gotopage - 1;
			
			try {
				
				vc.setPageIndex(pageIndex);
			
			} catch (PageIndexOutOfBoundsException e) {
				// TODO Auto-generated catch block
				
				logger.warn("PageIndexOutOfBoundsException : " + e.getMessage());
				
				try {
					vc.setPageIndex(0);
				} catch (PageIndexOutOfBoundsException e1) {
					// TODO Auto-generated catch block
					
				}
				
			}
		}			
	}

	private void initWebCarrierValue(WebCarrier wvc , HttpServletRequest request , PaginationKeywordFilter keywordFilter , Map para) {

		Set parameters = para.keySet();

		String parameterName = null;
		
		// parse parameter from http request
		for (Iterator iter = parameters.iterator() ; iter.hasNext();) {
			
			parameterName = (String)iter.next();
			
			if (!keywordFilter.isKeyword(parameterName)) {
				// parse parameter from http request
				
				String[] parameterValues = request.getParameterValues(parameterName);
				
				if (parameterValues != null) {
					
					// ---------- set the parameter for values 
					if (parameterValues.length == 1) {
						// change to class
						setParameterObjectValue(wvc , parameterValues[0] , parameterName);
					}
					
					else {
						wvc.setParameterValues(parameterName,parameterValues);
					}
					
				}
				
				
			}
		}		
	}
	
	private void initSingleSortValue(SingleSort ss , HttpServletRequest request ) {
		String sortByField = request.getParameter(PaginationKeyword.SINGLESORTBY_FIELD);
		if (sortByField != null) {
			String[] sortByFieldArrays = sortByField.split(" ");
			
			SortField sortField = new SortField();
			// field value
			sortField.setField(sortByFieldArrays[0].trim());
			
			// sort by type
			if (sortByFieldArrays.length == 2) {
				if (sortByFieldArrays[1].trim().equalsIgnoreCase("desc")) {
					sortField.setSortOrder(SortField.DESCENDING);
				}
				else if (sortByFieldArrays[1].trim().equalsIgnoreCase("asc")){
					sortField.setSortOrder(SortField.ASCENDING);
				}
			} else {
				sortField.setSortOrder(SortField.ASCENDING);
			}
			
			ss.setSortField(sortField);
		}			
	}
	
	private void initRecordLimitCarrier(RecordLimitCarrier rlc, HttpServletRequest request) throws PageIndexOutOfBoundsException {
		// --- get the page size from properties ---
		// parse special parameter
		String limitRecord = request.getParameter(PaginationKeyword.LIMIT_NAME);
		String startRecord = request.getParameter( PaginationKeyword.START_NAME );
		
		int limit = PaginationConstant.DEFAULT_LIMIT;
		try {
			if (limitRecord != null) {
				limit = Integer.parseInt( limitRecord );
			}
			else {
				String limStr =	envProperties.getProperty(EnvironmentProperty.LIMIT);
				if (limStr != null) {
					limit = Integer.parseInt( limStr );;
				}
			}
		} catch (NumberFormatException e) {
			// TODO Auto-generated catch block
			logger.info("limit number is error! please check it!");
		}		

		if (limit < 0) {
			throw new PageIndexOutOfBoundsException("the limit value is lower zero! please check it!");
		}

		try {
			rlc.setLimit(limit);
		} catch (RecordPaginationException e1) {
			// TODO Auto-generated catch block
			throw new PageIndexOutOfBoundsException(e1);
		}
		
		
		// --- start ---
		int start = 0;
		if (startRecord != null) {
			try {
				start = Integer.parseInt(startRecord);
			} catch (NumberFormatException e2) {
				// TODO Auto-generated catch block
				logger.info("start number is error! please check it!");
			}			
		}
		
		try {
			
			if (start < 0 ) {
				rlc.setStartIndex(0);
			} else {
				rlc.setStartIndex(start);
			}
			
		} catch (RecordPaginationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		
	}

	private void initRecordLimitSingleSortValue(SingleSort ss , HttpServletRequest request ) {
		String sortByField = request.getParameter(PaginationKeyword.SINGLESORTBY_FIELD);
		String dirField = request.getParameter( PaginationKeyword.DIR_NAME );
		
		if (sortByField != null) {
			
			SortField sortField = new SortField();
			// field value
			sortField.setField(sortByField.trim());
			
			// sort by type
			if (dirField != null ) {
				if ( dirField.trim().equalsIgnoreCase("desc") ) {
					sortField.setSortOrder(SortField.DESCENDING);
				}
				else if ( dirField.trim().equalsIgnoreCase("asc") ) {
					sortField.setSortOrder(SortField.ASCENDING);
				}
			} else {
				sortField.setSortOrder(SortField.ASCENDING);
			}

			
			ss.setSortField(sortField);
		}			
	}

	public Properties getConfiguration() {
		// TODO Auto-generated method stub
		return this.envProperties;
	}

	public void setConfiguration(Properties props) {
		// TODO Auto-generated method stub
		this.envProperties = props;
		
	}

	public PaginationWrapper generatePaginationWrapper() {
		// TODO Auto-generated method stub
		return null;
	}

	
	
}
